package sunlight.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Provider;
import java.util.InvalidPropertiesFormatException;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import sunlight.logger.EventLogger;
import sunlight.logger.Logger;
import sunlight.session.Session;
import sunlight.ui.UIController;
import sunlight.util.config.ConfigurationFile;
import sunlight.util.config.EncryptedLocalConfigurationFile;
import sunlight.util.config.EncryptedRemoteConfigurationFile;
import sunlight.util.config.LocalConfigurationFile;
import sunlight.util.config.RemoteConfigurationFile;



/**
 * Clasa Application iniţializează variabilele implicite de care face uz framework-ul si mai apoi aplicaţia.
 * Pentru a utiliza toate facilităţile trebuie sa extindeti această clasa, si sa apelaţi metode init(),
 * desigur după ce aţi apelat metoda set... pentru configuraţia dorită.
 * 
 * @author 	Adrian Şilimon
 * @version 1.0
 *
 */
public class Application extends Thread{
	
	/**
	 * calea către fişierul de configurare local
	 */
	public static String ConfigurationFilePath;
	
	/**
	 * URL-ul catre fişierul de configurare la distanţă
	 */
	public static String ConfigurationFileURI;
	
	/**
	 * tipul fişierului de configurare/proprietaţi; una dintre valorile, text plan sau xml 
	 * ConfigurationFile.CONFIG_FILE_PLAIN sau ConfigurationFile.CONFIG_FILE_XML
	 */
	public static int 	 ConfigurationFileType;
	
	/**
	 * cheia după care este criptat fişierul de configurare, 
	 * daca este criptat desigur
	 */
	public static String ConfigurationFileEncKey;
	
	/**
	 * algoritmul după care este criptat fişierul de configurare
	 */
	public static String ConfigurationFileEncAlgorithm;
	
	/**
	 * referinţa către fişierul de configuraţii
	 */
	protected static ConfigurationFile Config;
	
	/**
	 * referinţă către calea relativa a pachetului in care sunt stocate fisierele
	 * ResourceBundle pentru localizarea aplicaţiei
	 */
	public static String LanguageResBundlePackage = "sunlight.lang";
	
	/**
	 * referinta statica catre obiectul care se ocupa cu managementul 
	 * interfetei cu utilizatorul
	 */
	public static UIController UIControl;
	
	/**
	 * referinta catre obiectul care se ocupa cu inregistrarea erorilor
	 * critice ale programului (care nu pot fi afisate direct itilizatorui)
	 */
	public static EventLogger Log;
	
	/**
	 * referinta catre un obiect care incarcă resursele (ResourceBundle)
	 *  specifice localizarii(limbii) curente
	 */
	public static ResourceBundle Language;
	
	/**
	 * referinta statica catre obiectul care are rol de sesiune
	 */
	public static Session Session;
	
	/**
	 * timpul de blocare implicit pentru o resursa
	 */
	public static int ResourceDefaultLockingTime = 60;
	
	/**
	 * aplică calea către pachetul cu fişierele de localizare
	 * 
	 * @param packagename numele pachetului
	 */
	public void setLanguagesBundlesPackage(String packagename){
		LanguageResBundlePackage = packagename;
	}
	
	/**
	 * aplică parametrii fişierului de iniţializare local
	 * 
	 * @param path calea, ralativa sau absoluta către fişier
	 * @param type tipul fişierului plain sau xml (vezi interfaţa {@link:ConfigurationFile})
	 */
	public void setLocalConfigurationFile(String path, int type){
		ConfigurationFilePath = path;
		
		if((type == ConfigurationFile.CONFIG_FILE_PLAIN) || 
			(type == ConfigurationFile.CONFIG_FILE_XML)){
			ConfigurationFileType = type;
			
			try {
				Application.Config = new LocalConfigurationFile(new File(ConfigurationFilePath), ConfigurationFileType);
				
			} catch (InvalidPropertiesFormatException e) {
				System.err.println(e.toString());
			} catch (FileNotFoundException e) {
				System.err.println(e.toString());
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
		else{
			System.err.println("Tipul fisierului de configurare este gresit specificat!");
		}
	}
	
	/**
	 * aplică parametrii fişierului de configurare la distanţa
	 * 
	 * @param url	URL-ul la care se găseşte fişierul
	 * @param type	tipul de fişier (vezi interfaţa {@link ConfigurationFile})
	 */
	public void setRemoteConfigurationFile(String url, int type){
		ConfigurationFileURI = url;
		
		if((type == ConfigurationFile.CONFIG_FILE_PLAIN) || 
			(type == ConfigurationFile.CONFIG_FILE_XML)){
			ConfigurationFileType = type;
			
			try {
				Application.Config = new RemoteConfigurationFile(new URL(ConfigurationFileURI), ConfigurationFileType);
				
			} catch (InvalidPropertiesFormatException e) {
				System.err.println(e.toString());
			} catch (MalformedURLException e) {
				System.err.println(e.toString());
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
		else{
			System.err.println("Tipul fisierului de configurare este gresit specificat!");
		}
	}
	
	/**
	 * aplică parametrii fişierului de configurare local criptat
	 * 
	 * @param path 			calea către fişier
	 * @param type			tipul fişierului
	 * @param key			cheia de criptare (necesară decriptării)
	 * @param algorithm		algoritmul folosit la criptare
	 */
	public void setLocalEncryptedConfigurationFile(String path, int type, String key, String algorithm){
		ConfigurationFilePath = path;
		
		if((type == ConfigurationFile.CONFIG_FILE_PLAIN) || 
			(type == ConfigurationFile.CONFIG_FILE_XML)){
			ConfigurationFileType 			= type;
			ConfigurationFileEncKey 	   	= key;
			ConfigurationFileEncAlgorithm 	= algorithm;
			
			try {
				Application.Config = new EncryptedLocalConfigurationFile(new File(ConfigurationFilePath), ConfigurationFileType, ConfigurationFileEncKey, ConfigurationFileEncAlgorithm);
			
			} catch (InvalidPropertiesFormatException e) {
				System.err.println(e.toString());
			} catch (FileNotFoundException e) {
				System.err.println(e.toString());
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
		else{
			System.err.println("Tipul fisierului de configurare este gresit specificat!");
		}
	}
	
	/**
	 * aplică parametrii fişierului de configurare local criptat
	 * 
	 * @param path			calea către fişier
	 * @param type			tipul fişierului
	 * @param JCEProvider	furnizorul (provider) de servicii de criptografie
	 * @param key			cheia de criptare (necesară decriptării)
	 * @param algorithm		algoritmul folosit la criptare
	 */
	public void setLocalEncryptedConfigurationFile(String path, int type, Provider JCEProvider, String key, String algorithm){
		ConfigurationFilePath = path;
		
		if((type == ConfigurationFile.CONFIG_FILE_PLAIN) || 
			(type == ConfigurationFile.CONFIG_FILE_XML)){
			ConfigurationFileType 			= type;
			ConfigurationFileEncKey 	   	= key;
			ConfigurationFileEncAlgorithm 	= algorithm;
			
			try {
				Application.Config = new EncryptedLocalConfigurationFile(new File(ConfigurationFilePath), ConfigurationFileType, JCEProvider, ConfigurationFileEncKey, ConfigurationFileEncAlgorithm);
			
			} catch (InvalidPropertiesFormatException e) {
				System.err.println(e.toString());
			} catch (FileNotFoundException e) {
				System.err.println(e.toString());
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
		else{
			System.err.println("Tipul fisierului de configurare este gresit specificat!");
		}
	}
	
	/**
	 * aplică parametrii fişierului de configurare la distanţă criptat
	 * 
	 * @param url		URL-ul către fişier
	 * @param type		tipul fişierului
	 * @param key		cheia de criptare (necesară decriptării)
	 * @param algorithm	algoritmul folosit la criptare
	 */
	public void setRemoteEncryptedConfigurationFile(String url, int type, String key, String algorithm){
		ConfigurationFileURI = url;
		
		if((type == ConfigurationFile.CONFIG_FILE_PLAIN) || 
			(type == ConfigurationFile.CONFIG_FILE_XML)){
			ConfigurationFileType 			= type;
			ConfigurationFileEncKey 	   	= key;
			ConfigurationFileEncAlgorithm 	= algorithm;
			
			try {
				Application.Config = new EncryptedRemoteConfigurationFile(new URL(ConfigurationFileURI), ConfigurationFileType, ConfigurationFileEncKey, ConfigurationFileEncAlgorithm);
			} catch (InvalidPropertiesFormatException e) {
				System.err.println(e.toString());
			} catch (FileNotFoundException e) {
				System.err.println(e.toString());
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
		else{
			System.err.println("Tipul fisierului de configurare este gresit specificat!");
		}
	}
	
	/**
	 * aplică parametrii fişierului de configurare la distanţă criptat 
	 * 
	 * @param url			URL-ul către fişier
	 * @param type			tipul fişierului
	 * @param JCEProvider	furnizorul (provider) de servicii de criptografie
	 * @param key			cheia de criptare (necesară decriptării)
	 * @param algorithm		algoritmul folosit la criptare
	 */
	public void setRemoteEncryptedConfigurationFile(String url, int type, Provider JCEProvider, String key, String algorithm){
		ConfigurationFileURI = url;
		
		if((type == ConfigurationFile.CONFIG_FILE_PLAIN) || 
			(type == ConfigurationFile.CONFIG_FILE_XML)){
			ConfigurationFileType 			= type;
			ConfigurationFileEncKey 	   	= key;
			ConfigurationFileEncAlgorithm 	= algorithm;
			
			try {
				Application.Config = new EncryptedRemoteConfigurationFile(new URL(ConfigurationFileURI), ConfigurationFileType, JCEProvider, ConfigurationFileEncKey, ConfigurationFileEncAlgorithm);
			
			} catch (InvalidPropertiesFormatException e) {
				System.err.println(e.toString());
			} catch (FileNotFoundException e) {
				System.err.println(e.toString());
			} catch (IOException e) {
				System.err.println(e.toString());
			}
		}
		else{
			System.err.println("Tipul fisierului de configurare este gresit specificat!");
		}
	}
	
	/**
	 * aplică configuraţia/iniţializara 
	 * 
	 * @param config obiectul de iniţializare
	 */
	public void setConfiguration(ConfigurationFile config){
		Application.Config = config;
	}
	
	/**
	 * iniţializează variabilele
	 */
	public void init(){
		try {
			String loggerType;
			
			if(Application.Config == null){
				Application.Config = new LocalConfigurationFile(new File(ConfigurationFilePath), ConfigurationFileType);
			}
           
			loggerType = (String) Application.Config.get("logging.type");
            	
            if(loggerType.equals("local")){
            	Log = Logger.getLocalLogger((String) Application.Config.get("logging.dir"));
            }
    
            if(loggerType.equals("remote")){
            	Log = Logger.getRemoteLogger((String) Application.Config.get("logging.remote_address"), Integer.parseInt((String) Application.Config.get("logging.remote_port")));
            }
			
            Session	= new Session();
			Language= ResourceBundle.getBundle(LanguageResBundlePackage);
			
		} catch (IOException e) {
			System.err.println("Stream-ul de jurnalizare nu poate fi deschis! Detalii: " + e.toString());
		}
		catch(MissingResourceException mre){
			Log.write("Resursa lipsa: "+ mre.toString() , EventLogger.ERROR);
		}
		
		UIControl = new UIController();
		UIControl.init();
	}
	
	/**
	 * retunrează obiectul configuraţie 
	 * 
	 * @return ConfigurationFile
	 */
	public static ConfigurationFile getConfiguration(){
		return Application.Config;
	}
	
	/**
	 * inchide/ieşire
	 */
	public static void exit(){
		Application.Log.close();
		System.exit(0);
	}

}
